﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syngine.Components;
using Syngine.Components.Screens;
using Syngine.Graphics;

namespace Syngine
{
    /// <summary>
    /// 
    /// </summary>
    public class GameController : DrawableGameComponent
	{
		#region Fields

		private readonly ScreenController _screenController;
		private readonly GraphicsDeviceManager _graphics;
		private SpriteBatch _spriteBatch;

		#endregion

		#region Constructors

		/// <summary>
		/// 
		/// </summary>
		/// <param name="game"></param>
		public GameController(Game game) : base(game)
		{
			_graphics = new GraphicsDeviceManager(game);
			_screenController = new ScreenController(this);
        }

		#endregion

		#region Properties

	    /// <summary>
	    /// 
	    /// </summary>
		public event Action<DrawContext> PreDraw;

		/// <summary>
		/// 
		/// </summary>
		public event Action<DrawContext> PostDraw;

		/// <summary>
		/// 
		/// </summary>
		public SpriteBatch SpriteBatch
		{
			get
			{
				return _spriteBatch;
			}
		}

	    /// <summary>
	    /// 
	    /// </summary>
	    public GraphicsDeviceManager GraphicsDeviceManager
	    {
		    get
		    {
			    return _graphics;
		    }
	    }

	    public ScreenController ScreenController
	    {
		    get
		    {
			    return _screenController;
		    }
	    }

		#endregion

		#region Methods

		/// <summary>
		/// 
		/// </summary>
		/// <param name="screen"></param>
		public virtual void AddScreen(Screen screen)
		{
			ScreenController.Add(screen);
		}

	    /// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		public override void Initialize()
		{
			GraphicsSettings.SetFullScreen(true);
			GraphicsSettings.SetResolution(GraphicsDevice.DisplayMode.Width, GraphicsDevice.DisplayMode.Height);
		    GraphicsSettings.SetSupportedOrientations(DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight);
			_spriteBatch = new SpriteBatch(GraphicsDevice);

			GameContext.Input.Initialize();
			GameContext.Camera.Initialize();
			ScreenController.Initialize();

			base.Initialize();
		}

	    /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
	    {
			ScreenController.LoadContent(GameContext.CreateLoadContext());
			base.LoadContent();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
		{
			ScreenController.UnloadContent();
			base.UnloadContent();
        }

		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		public override void Update(GameTime gameTime)
		{
			var context = GameContext.CreateUpdateContext(gameTime);

			GameContext.Input.Update(context);
			GameContext.Camera.Update(context);
			ScreenController.Update(context);
			base.Update(gameTime);
		}

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
	        var drawContext = GameContext.CreateDrawContext(gameTime);

			OnPreDraw(drawContext);
			_spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.LinearWrap, null, null, null, GameContext.Camera.Projection);
			ScreenController.Draw(drawContext);
			base.Draw(gameTime);
			_spriteBatch.End();
			OnPostDraw(drawContext);
        }

		protected void OnPreDraw(DrawContext context)
		{
			var handler = PreDraw;

			if (handler != null)
			{
				handler(context);
			}
		}

		protected void OnPostDraw(DrawContext context)
	    {
		    var handler = PostDraw;

		    if (handler != null)
		    {
			    handler(context);
		    }
	    }

		#endregion
	}
}
